/*
 * Copyright 2008 Mark Davidson.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.doofer

import com.documentum.com.*
import com.documentum.fc.common.*
import com.documentum.fc.client.*

/**
 * Bootstraps the DFC extensions
 */
class Bootstrap {

    def au = new AttribUtil()
    def cu = new ContentUtil()
    def ou = new ObjectUtil()
    def qu = new QueryUtil()
    def tu = new TypeUtil()

    /**
     * Constructor
     */
    Bootstrap(){}
    
    /**
     * Entry point to boot routines
     */
    def boot() {

        if(!IDfClientX.metaClass.getMetaProperty('dooferVersion')) {
            if(DfLogger.isDebugEnabled(this)) DfLogger.debug(this, "Boostrapping DFC extensions...", null, null)

            ExpandoMetaClass.enableGlobally()

            extendIDfClientX()
            extendIDfSession()
            extendIDfType()
            extendIDfTypedObject()
            extendIDfPersistentObject()

            if(DfLogger.isDebugEnabled(this)) DfLogger.debug(this, "Completed bootstrapping.", null, null)
        }
    }

    /**
     * Adds extensions to IDfClientX
     */
    private def extendIDfClientX() {

        // set doofer version
        IDfClientX.metaClass.getDooferVersion = { -> "0.1-beta" }
    }

    /**
     * Adds extensions to IDfSession
     */
    private def extendIDfSession() {

        // add overloaded getObject() method - now features map based param (keys are objectId/path/qualification)
        IDfSession.metaClass.getObject = { Map params -> ou.getObject(params + [session: delegate]) }

        // add overloaded getObject() method - now features string based object id param
        IDfSession.metaClass.getObject = { String objectId -> delegate.getObject((new DfId(objectId))) }

        // add getSysObject() method
        IDfSession.metaClass.getSysObject = { IDfId id -> delegate.getObject(id) as IDfSysObject }

        // add getSysObject() method - uses string param
        IDfSession.metaClass.getSysObject = { String objectId -> delegate.getObject(new DfId(objectId)) as IDfSysObject }

        // add getSysObjectByPath() method
        IDfSession.metaClass.getSysObjectByPath = { String path -> delegate.getObjectByPath(path) as IDfSysObject }

        // add getSysObjectByqualification() method
        IDfSession.metaClass.getSysObjectByQualification = { String qual -> delegate.getObjectByQualification(qual) as IDfSysObject }

        // add getDocument() method
        IDfSession.metaClass.getDocument = {  IDfId id -> delegate.getObject(id) as IDfDocument }

        // add getDocument() method
        IDfSession.metaClass.getDocument = {  String objectId -> delegate.getObject(new DfId(objectId)) as IDfDocument }        

        // add getDocumentByPath() method
        IDfSession.metaClass.getDocumentByPath = {  String path -> delegate.getObjectByPath(path) as IDfDocument }

        // add getDocumentByQualification() method
        IDfSession.metaClass.getDocumentByQualification = {  String qual -> delegate.getObjectByQualification(qual) as IDfDocument }

        // add useTrans() method
        IDfSession.metaClass.useTrans = { Closure closure ->
            try {
                if(!delegate.transactionActive) delegate.beginTrans()
                closure()
                if(delegate.transactionActive) delegate.commitTrans()
            } catch(Throwable t) {
                if(delegate.transactionActive) delegate.abortTrans()
                throw t
            }
        }

        // add query() method
        IDfSession.metaClass.query = { Map params, Closure closure ->
            qu.execute(params + [session: delegate], closure)
        }

        // add query() method
        IDfSession.metaClass.query = { Map params ->
            qu.execute(params + [session: delegate])
        }

        // add queryForList() method
        IDfSession.metaClass.queryForList = { Map params ->
            qu.toList(params + [session: delegate])
        }

        // add queryForList() method
        IDfSession.metaClass.queryForList = { Map params, Closure closure ->
            qu.toList(params + [session: delegate], closure)
        }

        // add walk() method
        IDfSession.metaClass.walk = { Map params, Closure closure ->
            qu.walk(params + [session: delegate], closure)
        }

        // add walkObjects() mathod
        IDfSession.metaClass.walkObjects = { Map params, Closure closure ->
            ou.walkObjects(params + [session: delegate], closure)
        }

        // add deltree() method
        IDfSession.metaClass.deltree = { Map params ->
            ou.deltree(params + [session: delegate])
        }

        // add createObject() method
        IDfSession.metaClass.createObject = { Map params ->
            ou.createObject(params + [session: delegate])
        }

        // add createObject() method
        IDfSession.metaClass.createObject = { Map params, Closure closure ->
            ou.createObject(params + [session: delegate], closure)
        }

        // add createFolder() method
        IDfSession.metaClass.createFolder = { Map params ->
            ou.createFolder(params + [session: delegate])
        }

        // add createFolder() method
        IDfSession.metaClass.createFolder = { Map params, Closure closure ->
            ou.createFolder(params + [session: delegate], closure)
        }

        // add importDocument() method
        IDfSession.metaClass.importDocument = { Map params, Closure closure ->
            ou.importDocument(params + [session: delegate], closure)
        }

        // add importDocument() method
        IDfSession.metaClass.importDocument = { Map params ->
            ou.importDocument(params + [session: delegate])
        }
    }

    /**
     * Adds extensions to IDfType
     */
    private def extendIDfType() {
        // add getChildTypes() method
        IDfType.metaClass.getChildTypes = { ->
            tu.getChildTypes([typeName: delegate.name, session: delegate.session])
        }

        // add hasChildType() method
        IDfType.metaClass.hasChildType = { String childTypeName ->
            tu.hasChildType([typeName: delegate.name, child: childTypeName, session: delegate.session])
        }

        // add getParentTypes() method
        IDfType.metaClass.getParentTypes = { ->
            tu.getParentTypes([typeName: delegate.name, session: delegate.session])
        }

        // add hasParentType() method
        IDfType.metaClass.hasParentType = { String parentTypeName ->
            tu.hasParentType([typeName: delegate.name, parent: parentTypeName, session: delegate.session])
        }
    }

    /**
     * Adds extensions to IDfTypedObject
     */
    private def extendIDfTypedObject() {

        // add slicing capability for attributes
        IDfTypedObject.metaClass.getAt = { int index ->
            def numAttribs = delegate.getAttrCount()
            if(index < numAttribs) {
                return au.readAttrib(object: delegate, attribName: delegate.getAttr(index).name)
            } else {
                return null
            }
        }

        // add size method
        IDfTypedObject.metaClass.size = { delegate.attrCount }

        // add read property capability for attributes
        IDfTypedObject.metaClass.propertyMissing = { String name ->
            au.readAttrib(object: delegate, attribName: name)
        }

        // add ability to output all attribute values to map
        IDfTypedObject.metaClass.getMap = { ->
            au.readAttribs(object: delegate)
        }

        // add ability to output specified attrib values to map
        IDfTypedObject.metaClass.getMap = { List attribNames ->
            au.readAttribs(object: delegate, attribNames: attribNames)
        }
    }

    /**
     * Adds extensions to IDfPersistentObject
     */
    private def extendIDfPersistentObject() {

        // add write property capability for attributes
        IDfPersistentObject.metaClass.propertyMissing = { String name, value ->
            au.writeAttrib(object: delegate, attribName: name, attribValue: value)
        }

        // add writeAttribs() method
        IDfPersistentObject.metaClass.setAttribs = { Map attribs ->
            for(attrib in attribs) {
                au.writeAttrib(object: delegate, attribName: attrib.key, attribValue: attrib.value)
            }
        }

        // add left shift (i.e. "<<" ) capability to write attribute values
        IDfPersistentObject.metaClass.leftShift = { Map attribs ->
            for(attrib in attribs) {
                au.writeAttrib(object: delegate, attribName: attrib.key, attribValue: attrib.value)
            }
        }

        // overload getContent() with Map parameter (i.e. named params)
        IDfPersistentObject.metaClass.getContent = { Map params ->
            cu.getContent([object: delegate] + params)
        }

        // add ability to get text from a persistent object
        IDfPersistentObject.metaClass.getText = { ->
            cu.getText(object: delegate)
        }

        // add ability to get text from a persistent object
        IDfPersistentObject.metaClass.getText = { Map params ->
            cu.getText([object: delegate] + params)
        }

        // add toFile() method
        IDfPersistentObject.metaClass.toFile = { Map params ->
            cu.exportContent([object: delegate] + params)
        }
    }

}